using System;
using System.Web;

using Microsoft.SharePoint;

using RealDolmen.SharePoint.Helpers;

namespace RealDolmen.SharePoint.ListFeatures
{
    public abstract class ListFeatureTracker
    {
        public abstract ListFeature OpenListFeature(out SPWeb web, out SPSite site);
        
        public abstract string Data
        {
            get;
        }

        public override string ToString()
        {
            return Data;
        }

        public static ListFeatureTracker Instantiate(string trackerData)
        {
            bool needsUpdate = false;

            ListFeatureTracker tracker = GetTracker(trackerData);
            if (tracker != null)
            {
                needsUpdate = tracker.Data != trackerData;

                if (tracker != null && !(tracker is ListFeatureTrackerVersionTwo))
                {
                    tracker = ListFeatureTrackerVersionTwo.Convert(tracker);
                    needsUpdate = true;
                }

                if (needsUpdate)
                {
                    SPSite site;
                    SPWeb web;
                    ListFeature listFeature = tracker.OpenListFeature(out web, out site);
                    if (listFeature != null)
                    {
                        listFeature.UpdateTrackerDataReferences(trackerData);
                        SPHelper.TryDisposeWeb(web);
                        SPHelper.TryDisposeSite(site);
                    }
                }
            }
            return tracker;
        }

        public static ListFeatureTracker Instantiate(ListFeature listFeature)
        {
            return ListFeatureTrackerVersionTwo.Instantiate(listFeature);
        }

        private static ListFeatureTracker GetTracker(string trackerData)
        {
            if (ListFeatureTrackerVersionTwo.HasValidFormat(trackerData))
            {
                return ListFeatureTrackerVersionTwo.Instantiate(trackerData);
            }
            if (ListFeatureTrackerVersionOne.HasValidFormat(trackerData))
            {
                return ListFeatureTrackerVersionOne.Instantiate(trackerData);
            }
            return null;
        }
    }

    public class ListFeatureTrackerVersionOne : ListFeatureTracker
    {
        #region Variables
        private static string SEPARATORSTRING = ";#";

        private Guid listFeatureGuid;
        private string listUrl;
        #endregion // Variables

        #region Overrides
        public override string Data
        {
            get { return HttpUtility.UrlEncode(string.Format("{{{0}{2}{1}}}", listUrl, listFeatureGuid, SEPARATORSTRING)); }
        } 
        #endregion // Overrides

        protected ListFeatureTrackerVersionOne(){}

        #region Statics
        public static bool HasValidFormat(string trackerData)
        {
            string temp = HttpUtility.UrlDecode(trackerData);

            return temp.Contains(SEPARATORSTRING) && temp.StartsWith("{") && temp.EndsWith("}");
        }

        public static new ListFeatureTrackerVersionOne Instantiate(string trackerData)
        {
            ListFeatureTrackerVersionOne tracker = new ListFeatureTrackerVersionOne();

            if (tracker.InstantiateInternal(trackerData))
                return tracker;
            else
                return null;
        }

        public static new ListFeatureTrackerVersionOne Instantiate(ListFeature listFeature)
        {
            ListFeatureTrackerVersionOne tracker = new ListFeatureTrackerVersionOne();

            if (tracker.InstantiateInternal(listFeature))
                return tracker;
            else
                return null;
        } 
        #endregion // Statics

        #region Internals
        protected internal bool InstantiateInternal(string trackerData)
        {
            string decoded = HttpUtility.UrlDecode(trackerData);
            decoded = decoded.Substring(1, decoded.Length - 2);
            string[] splitted = decoded.Split(new string[] { SEPARATORSTRING }, StringSplitOptions.RemoveEmptyEntries);

            listUrl = splitted[0];
            listFeatureGuid = new Guid(splitted[1]);

            if (IsDataValidListFeature())
                return true;

            if (listUrl.StartsWith("http://"))
            {
                listUrl = "https" + listUrl.Substring(4);
                if (IsDataValidListFeature())
                    return true;
            }

            return false;
        }

        protected internal bool IsDataValidListFeature()
        {
            bool valid = false;
            try
            {
                SPSite site = new SPSite(listUrl);
                SPWeb web = site.OpenWeb();
                SPList list = web.GetList(listUrl);

                valid = ListFeature.Instantiate(listFeatureGuid, list, web) != null;

                SPHelper.TryDisposeWeb(web);
                SPHelper.TryDisposeSite(site);
            }
            catch { }
            return valid;
        }
        
        protected internal bool InstantiateInternal(ListFeature listFeature)
        {
            if (listFeature != null)
            {
                listUrl = SPHelper.GetAbsoluteUrl(listFeature.ParentList);

                return true;
            }
            return false;
        } 
        #endregion // Internals

        public override ListFeature OpenListFeature(out SPWeb web, out SPSite site)
        {
            site = new SPSite(listUrl);
            web = site.OpenWeb();
            SPList list = web.GetList(listUrl);

            ListFeature listFeature = ListFeature.Instantiate(listFeatureGuid, list, web);

            if (listFeature == null)
            {
                SPHelper.TryDisposeWeb(web);
                SPHelper.TryDisposeSite(site);
            }
            return listFeature;
        }
    }

    public class ListFeatureTrackerVersionTwo : ListFeatureTracker
    {
        #region Variables
        private Guid listFeatureGuid;
        private Guid siteCollectionGuid;
        private Guid siteGuid;
        private Guid listGuid;
        #endregion // Variables

        #region Overrides
        public override string Data
        {
            get { return string.Format("{0}{1}{2}{3}", siteCollectionGuid.ToString("N"), siteGuid.ToString("N"), listGuid.ToString("N"), listFeatureGuid.ToString("N")); }
        }
        #endregion // Overrides

        protected ListFeatureTrackerVersionTwo(){}

        #region Statics
        public static bool HasValidFormat(string trackerData)
        {
            return !trackerData.Contains("%") && trackerData.Length == 128;
        }

        public static new ListFeatureTrackerVersionTwo Instantiate(string trackerData)
        {
            ListFeatureTrackerVersionTwo tracker = new ListFeatureTrackerVersionTwo();

            if (tracker.InstantiateInternal(trackerData))
                return tracker;
            else
                return null;
        }

        public static new ListFeatureTrackerVersionTwo Instantiate(ListFeature listFeature)
        {
            ListFeatureTrackerVersionTwo tracker = new ListFeatureTrackerVersionTwo();

            if (tracker.InstantiateInternal(listFeature))
                return tracker;
            else
                return null;
        }
        public static ListFeatureTrackerVersionTwo Convert(ListFeatureTracker tracker)
        {
            if (tracker is ListFeatureTrackerVersionOne)
            {
                SPSite site;
                SPWeb web;
                ListFeatureTrackerVersionTwo trackerVersionTwo = null;
                ListFeatureTrackerVersionOne trackerVersionOne = (ListFeatureTrackerVersionOne) tracker;
                ListFeature listFeature = trackerVersionOne.OpenListFeature(out web, out site);
                if (listFeature != null)
                {
                    trackerVersionTwo = ListFeatureTrackerVersionTwo.Instantiate(listFeature);
                    SPHelper.TryDisposeWeb(web);
                    SPHelper.TryDisposeSite(site);
                }
            }
            if (tracker is ListFeatureTrackerVersionTwo)
            {
                return (ListFeatureTrackerVersionTwo)tracker;
            }
            return null;
        }
        #endregion // Statics

        #region Internals
        protected internal bool InstantiateInternal(string trackerData)
        {
            siteCollectionGuid = new Guid(trackerData.Substring(0, 32));
            siteGuid = new Guid(trackerData.Substring(32, 32));
            listGuid = new Guid(trackerData.Substring(64, 32));
            listFeatureGuid = new Guid(trackerData.Substring(96, 32));

            if (IsDataValidListFeature())
            {
                return true;
            }

            if (SPContext.Current != null)
            {
                if (listGuid == Guid.Empty)
                {
                    listGuid = SPContext.Current.ListId;
                    if (IsDataValidListFeature())
                    {
                        return true;
                    }
                }

                Guid oldSiteCollectionGuid = siteCollectionGuid;
                Guid oldSiteGuid = siteGuid;

                if (IsDataValidListFeature(SPContext.Current.Site.ID, oldSiteGuid)
                    || IsDataValidListFeature(oldSiteCollectionGuid, SPContext.Current.Web.ID)
                    || IsDataValidListFeature(SPContext.Current.Site.ID, SPContext.Current.Web.ID))
                {
                    return true;
                }
            }
            return false;
        }

        protected internal bool IsDataValidListFeature(Guid siteCollectionGuid, Guid siteGuid)
        {
            this.siteCollectionGuid = siteCollectionGuid;
            this.siteGuid = siteGuid;
            return IsDataValidListFeature();
        }

        protected internal bool IsDataValidListFeature()
        {
            bool valid = false;
            try
            {
                SPSite site = new SPSite(siteCollectionGuid);
                SPWeb web = site.OpenWeb(siteGuid);
                SPList list = web.Lists[listGuid];

                valid = ListFeature.Instantiate(listFeatureGuid, list, web) != null;

                SPHelper.TryDisposeWeb(web);
                SPHelper.TryDisposeSite(site);
            }
            catch { }
            return valid;
        }

        public override ListFeature OpenListFeature(out SPWeb web, out SPSite site)
        {
            site = new SPSite(siteCollectionGuid);
            web = site.OpenWeb(siteGuid);
            SPList list = web.Lists[listGuid];

            ListFeature listFeature = ListFeature.Instantiate(listFeatureGuid, list, web);

            if (listFeature == null)
            {
                SPHelper.TryDisposeWeb(web);
                SPHelper.TryDisposeSite(site);
            }
            return listFeature;
        }

        protected internal bool InstantiateInternal(ListFeature listFeature)
        {
            if (listFeature != null)
            {
                listFeatureGuid = listFeature.Id;
                listGuid = listFeature.ParentList.ID;

                SPWeb web = listFeature.ParentList.ParentWeb;
                siteGuid = web.ID;
                siteCollectionGuid = web.Site.ID;

                return true;
            }
            return false;
        } 
        #endregion // Internals
    }

    public interface IListFeatureTrackerReferencee
    {
        void UpdateTrackerReference(ListFeature listFeature, string oldTrackerData);
    }
}
